added CIE Lab and YCbCr models
authorØyvind Kolås <ok@src.gnome.org>
Tue, 16 Aug 2005 07:38:22 +0000 (07:38 +0000)
committerØyvind Kolås <ok@src.gnome.org>
Tue, 16 Aug 2005 07:38:22 +0000 (07:38 +0000)
16 files changed:
ChangeLog
babl/babl-fish.c
babl/babl-ids.h
babl/base/Makefile.am
babl/base/babl-base.c
babl/base/cpercep.c [new file with mode: 0644]
babl/base/cpercep.h [new file with mode: 0644]
babl/base/model-cmyk.c
babl/base/model-lab.c [new file with mode: 0644]
babl/base/model-rgb.c
babl/base/model-ycbcr.c
babl/base/type-u16.c
tests/Makefile.am
tests/rgb_to_lab_to_rgb.c [new file with mode: 0644]
tests/rgb_to_ycbcr.c [new file with mode: 0644]
tests/rgb_to_ycbcr_to_rgb.c [new file with mode: 0644]

index 0e461a3290c72e78c624f30eda49188778db421e..ed4f524bd482ba52c479ddd5f9c3d915c724af4c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2005-08-16  Øyvind Kolås  <pippin@gimp.org>
+
+       * babl/babl-fish.c: reindentation, no naming of fishes.
+       * babl/babl-ids.h: reordering, added YCBCRA, LAB and LAB_ALPHA
+       * babl/base/Makefile.am: added cpercep.[ch] model-lab.c and
+       model-ycbcr.c
+       * babl/base/cpercep.[ch]: *NEW*
+       * babl/base/model-lab.c: *NEW* CIE Lab color model, backed by a slightly
+       modified version of cpercep from gimp sources (no gamma correction and
+       normalized RGB values)
+       * babl/base/model-ycbcr: made ycbcr work, numbers still need to be
+       verified.
+       * babl/base/babl-base.c: initialize CIE Lab and YCbCr
+       * babl/base/model-cmyk.c: slight changes, it still doesn't pass
+       rgb->cmyk->rgb testing so not included yet.
+       * babl/base/model-rgb.c: make rgba-double use BABL_DOUBLE instead of
+       BABL_FLOAT
+       * babl/base/type-u16: fix conversion to double.
+       * tests/Makefile.am: added rgb_to_lab_to_rgb , rgb_to_ycbcr_to_rgb and
+       rgb_to_ycbcr
+       * tests/rgb_to_lab_to_rgb.c
+       * tests/rgb_to_ycbcr_to_rgb.c
+       * tests/rgb_to_ycbcr.c: *NEW* tests
+
 2005-08-15  Øyvind Kolås  <pippin@gimp.org>
 
        * babl/base/model-cmyk.c
index 0e376afd39925efbbf31af9ee6123d61ded54948..d8d4a8ad35851063ba4008109a47d386c5d53f0f 100644 (file)
@@ -100,15 +100,16 @@ BablConversion *babl_conversion_find (Babl *source,
                                       Babl *destination)
 {
   SearchData data;
-  data.source       = source;
+  data.source      = source;
   data.destination = destination;
-  data.result = NULL;
+  data.result      = NULL;
   babl_conversion_each (find_conversion, &data);
 
   if (!data.result)
     {
-      babl_log ("%s('%s', '%s'): failed", __FUNCTION__,
+      babl_log ("%s('%s', '%s'): failed, aborting", __FUNCTION__,
         source->instance.name, destination->instance.name);
+      exit (-1);
       return NULL;
     }
   return data.result;
@@ -126,7 +127,7 @@ babl_fish_reference_new (Babl *source,
   babl                   = babl_calloc (sizeof (BablFishReference), 1);
   babl->class_type       = BABL_FISH_REFERENCE;
   babl->instance.id      = 0;
-  babl->instance.name    = "Fishy";
+  babl->instance.name    = NULL;
   babl->fish.source      = (union Babl*)source;
   babl->fish.destination = (union Babl*)destination;
 
@@ -203,6 +204,7 @@ babl_fish_process (BablFish *babl_fish,
   fooA = babl_malloc(sizeof (double) * n * 4); 
   fooB = babl_malloc(sizeof (double) * n * 4); 
 
+  assert (babl_fish);
   assert (source);
   assert (destination);
 
index 84656b33b90910f79e5de0836ed234dea1950441..9f6f3314368932f0cd35277dcd0743d2f9e74fba 100644 (file)
@@ -64,14 +64,17 @@ enum {
   BABL_RGBA,
   BABL_RGBA_GAMMA_2_2,
   BABL_RGBA_PREMULTIPLIED,
-  BABL_CMY,
-  BABL_CMYK,
-  BABL_CMYKA,
-  BABL_YCBCR,
   BABL_GRAYSCALE,
   BABL_GRAYSCALE_GAMMA_2_2,
   BABL_GRAYSCALE_ALPHA,
   BABL_GRAYSCALE_ALPHA_PREMULTIPLIED,
+  BABL_YCBCR,
+  BABL_YCBCRA,
+  BABL_LAB,
+  BABL_LAB_ALPHA,
+  BABL_CMY,
+  BABL_CMYK,
+  BABL_CMYKA,
 
   BABL_PIXEL_FORMAT_BASE = 100000,
   BABL_SRGB,
index c31d8a579002d811d831f529df66b33a62e83cc8..90147772772252d786ecadacf5b54e0c1c46874b 100644 (file)
@@ -1,14 +1,18 @@
 h_sources =                    \
-       babl-base.h
+       babl-base.h             \
+       cpercep.h
 
 c_sources =                    \
        babl-base.c             \
+       cpercep.c               \
        type-double.c           \
        type-float.c            \
        type-u8.c               \
        type-u16.c              \
        model-rgb.c             \
-       model-grayscale.c
+       model-grayscale.c       \
+       model-lab.c             \
+       model-ycbcr.c
 
 INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/babl
 
@@ -19,6 +23,5 @@ EXTRA_DIST :=                 \
        .cvsignore      \
        rgb-constants.h \
        util.h
-       
 
 LDADD = -lm
index 8d4ea7eca89e1fab93836f0e48b801329972aacc..68050cd302ef9ce6b43f1d3a5d4b1eb3300cc4ad 100644 (file)
@@ -64,6 +64,8 @@ types (void)
 
 void babl_base_model_rgb       (void);
 void babl_base_model_grayscale (void);
+void babl_base_model_ycbcr     (void);
+void babl_base_model_lab       (void);
 
 static void
 models (void)
@@ -71,4 +73,7 @@ models (void)
   babl_base_model_rgb       (); /* must be registered first since it is the
                                    reference */
   babl_base_model_grayscale ();
+  babl_base_model_lab       ();
+  babl_base_model_ycbcr     ();
 }
+
diff --git a/babl/base/cpercep.c b/babl/base/cpercep.c
new file mode 100644 (file)
index 0000000..055bf95
--- /dev/null
@@ -0,0 +1,652 @@
+/*
+Copyright (C) 1999-2002 Adam D. Moss (the "Author").  All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Author of the
+Software shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization
+from the Author.
+*/
+
+/*
+  cpercep.c: The CPercep Functions v0.9: 2002-02-10
+  Adam D. Moss: adam@gimp.org <http://www.foxbox.org/adam/code/cpercep/>
+
+  This code module concerns itself with conversion from a hard-coded
+  RGB colour space (sRGB by default) to CIE L*a*b* and back again with
+  (primarily) precision and (secondarily) speed, oriented largely
+  towards the purposes of quantifying the PERCEPTUAL difference between
+  two arbitrary RGB colours with a minimum of fuss.
+
+  Motivation One: The author is disheartened at the amount of graphics
+  processing software around which uses weighted or non-weighted
+  Euclidean distance between co-ordinates within a (poorly-defined) RGB
+  space as the basis of what should really be an estimate of perceptual
+  difference to the human eye.  Certainly it's fast to do it that way,
+  but please think carefully about whether your particular application
+  should be tolerating sloppy results for the sake of real-time response.
+
+  Motivation Two: Lack of tested, re-usable and free code available
+  for this purpose.  The difficulty in finding something similar to
+  CPercep with a free license motivated this project; I hope that this
+  code also serves to illustrate how to perform the
+  R'G'B'->XYZ->L*a*b*->XYZ->R'G'B' transformation correctly since I
+  was distressed to note how many of the equations and code snippets
+  on the net were omitting the reverse transform and/or were using
+  incorrectly-derived or just plain wrong constants.
+
+  TODO: document functions, rename erroneously-named arguments
+*/
+
+/* defines added to make it compile outside gimp */
+
+#ifndef gboolean
+#define gboolean int
+#endif
+#ifndef FALSE
+#define FALSE    0
+#endif
+#ifndef TRUE
+#define TRUE     1
+#endif
+
+
+#include "config.h"
+#include <math.h>
+
+#ifndef __GLIBC__
+/* cbrt() is a GNU extension */
+#define cbrt(x) (pow(x, 1.0/3.0))
+#endif
+
+#include "cpercep.h"
+
+
+/* defines:
+
+   SANITY: emits warnings when passed non-sane colours (and usually
+   corrects them) -- useful when debugging.
+
+   APPROX: speeds up the conversion from RGB to the colourspace by
+   assuming that the RGB values passed in are integral and definitely
+   in the range 0->255
+
+   SRGB: assumes that the RGB values being passed in (and out) are
+   destined for an sRGB-alike display device (a typical modern monitor)
+   -- if you change this then you'll probably want to change ASSUMED_GAMMA,
+   the phosphor colours and the white point definition.
+*/
+
+/* #define SANITY */
+/* #define APPROX */
+/* #define SRGB   */
+
+
+#ifdef SRGB
+#define ASSUMED_GAMMA (2.2F)
+#else
+/*#define ASSUMED_GAMMA (2.591F)*/
+#define ASSUMED_GAMMA (1.0F)
+#endif
+
+#define REV_GAMMA ((1.0F / ASSUMED_GAMMA))
+
+
+/* define characteristics of the source RGB space (and the space
+   within which we try to behave linearly). */
+
+/* Phosphor colours: */
+
+/* sRGB/HDTV phosphor colours */
+static const double pxr = 0.64F;
+static const double pyr = 0.33F;
+static const double pxg = 0.30F;
+static const double pyg = 0.60F;
+static const double pxb = 0.15F;
+static const double pyb = 0.06F;
+
+/* White point: */
+
+/* D65 (6500K) (recommended but not a common display default) */
+static const double lxn = 0.312713F;
+static const double lyn = 0.329016F;
+
+/* D50 (5000K) */
+/*static const double lxn = 0.3457F; */
+/*static const double lyn = 0.3585F; */
+
+/* D55 (5500K) */
+/*static const double lxn = 0.3324F; */
+/*static const double lyn = 0.3474F; */
+
+/* D93 (9300K) (a common monitor default, but poor colour reproduction) */
+/* static const double lxn = 0.2848F; */
+/* static const double lyn = 0.2932F; */
+
+/* illum E (normalized) */
+/*static const double lxn = 1.0/3.0F; */
+/*static const double lyn = 1.0/3.0F; */
+
+/* illum C (average sunlight) */
+/*static const double lxn = 0.3101F; */
+/*static const double lyn = 0.3162F; */
+
+/* illum B (direct sunlight) */
+/*static const double lxn = 0.3484F; */
+/*static const double lyn = 0.3516F; */
+
+/* illum A (tungsten lamp) */
+/*static const double lxn = 0.4476F; */
+/*static const double lyn = 0.4074F; */
+
+
+static const double LRAMP = 7.99959199F;
+
+
+static double xnn, znn;
+
+static double powtable[256];
+
+
+#ifndef CLAMP
+#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x)))
+#endif
+
+
+static void
+init_powtable(const double gamma)
+{
+  int i;
+
+#ifndef SRGB
+  /* pure gamma function */
+  for (i=0; i<256; i++)
+    {
+      powtable[i] = pow((i)/255.0F, gamma);
+    }
+#else
+  /* sRGB gamma curve */
+  for (i=0; i<11 /* 0.03928 * 255 */; i++)
+    {
+      powtable[i] = (i) / (255.0F * 12.92F);
+    }
+  for (; i<256; i++)
+    {
+      powtable[i] = pow( (((i) / 255.0F) + 0.055F) / 1.055F, 2.4F);
+    }
+#endif
+}
+
+
+typedef double CMatrix[3][3];
+typedef double CVector[3];
+
+static CMatrix Mrgb_to_xyz, Mxyz_to_rgb;
+
+static int
+Minvert (CMatrix src, CMatrix dest)
+{
+  double det;
+
+  dest[0][0] = src[1][1] * src[2][2] - src[1][2] * src[2][1];
+  dest[0][1] = src[0][2] * src[2][1] - src[0][1] * src[2][2];
+  dest[0][2] = src[0][1] * src[1][2] - src[0][2] * src[1][1];
+  dest[1][0] = src[1][2] * src[2][0] - src[1][0] * src[2][2];
+  dest[1][1] = src[0][0] * src[2][2] - src[0][2] * src[2][0];
+  dest[1][2] = src[0][2] * src[1][0] - src[0][0] * src[1][2];
+  dest[2][0] = src[1][0] * src[2][1] - src[1][1] * src[2][0];
+  dest[2][1] = src[0][1] * src[2][0] - src[0][0] * src[2][1];
+  dest[2][2] = src[0][0] * src[1][1] - src[0][1] * src[1][0];
+
+  det =
+    src[0][0] * dest[0][0] +
+    src[0][1] * dest[1][0] +
+    src[0][2] * dest[2][0];
+
+  if (det <= 0.0F)
+    {
+#ifdef SANITY
+      g_printerr ("\n\007 XXXX det: %f\n", det);
+#endif
+      return 0;
+    }
+
+  dest[0][0] /= det;
+  dest[0][1] /= det;
+  dest[0][2] /= det;
+  dest[1][0] /= det;
+  dest[1][1] /= det;
+  dest[1][2] /= det;
+  dest[2][0] /= det;
+  dest[2][1] /= det;
+  dest[2][2] /= det;
+
+  return 1;
+}
+
+
+static void
+rgbxyzrgb_init(void)
+{
+  init_powtable (ASSUMED_GAMMA);
+
+  xnn = lxn / lyn;
+  /* ynn taken as 1.0 */
+  znn = (1.0F - (lxn + lyn)) / lyn;
+
+  {
+    CMatrix MRC, MRCi;
+    double C1,C2,C3;
+
+    MRC[0][0] = pxr;
+    MRC[0][1] = pxg;
+    MRC[0][2] = pxb;
+    MRC[1][0] = pyr;
+    MRC[1][1] = pyg;
+    MRC[1][2] = pyb;
+    MRC[2][0] = 1.0F - (pxr + pyr);
+    MRC[2][1] = 1.0F - (pxg + pyg);
+    MRC[2][2] = 1.0F - (pxb + pyb);
+
+    Minvert (MRC, MRCi);
+
+    C1 = MRCi[0][0]*xnn + MRCi[0][1] + MRCi[0][2]*znn;
+    C2 = MRCi[1][0]*xnn + MRCi[1][1] + MRCi[1][2]*znn;
+    C3 = MRCi[2][0]*xnn + MRCi[2][1] + MRCi[2][2]*znn;
+
+    Mrgb_to_xyz[0][0] = MRC[0][0] * C1;
+    Mrgb_to_xyz[0][1] = MRC[0][1] * C2;
+    Mrgb_to_xyz[0][2] = MRC[0][2] * C3;
+    Mrgb_to_xyz[1][0] = MRC[1][0] * C1;
+    Mrgb_to_xyz[1][1] = MRC[1][1] * C2;
+    Mrgb_to_xyz[1][2] = MRC[1][2] * C3;
+    Mrgb_to_xyz[2][0] = MRC[2][0] * C1;
+    Mrgb_to_xyz[2][1] = MRC[2][1] * C2;
+    Mrgb_to_xyz[2][2] = MRC[2][2] * C3;
+
+    Minvert (Mrgb_to_xyz, Mxyz_to_rgb);
+  }
+}
+
+
+static void
+xyz_to_rgb (double *inx_outr,
+           double *iny_outg,
+           double *inz_outb)
+{
+  const double x = *inx_outr;
+  const double y = *iny_outg;
+  const double z = *inz_outb;
+
+  *inx_outr = Mxyz_to_rgb[0][0]*x + Mxyz_to_rgb[0][1]*y + Mxyz_to_rgb[0][2]*z;
+  *iny_outg = Mxyz_to_rgb[1][0]*x + Mxyz_to_rgb[1][1]*y + Mxyz_to_rgb[1][2]*z;
+  *inz_outb = Mxyz_to_rgb[2][0]*x + Mxyz_to_rgb[2][1]*y + Mxyz_to_rgb[2][2]*z;
+}
+
+
+static void
+rgb_to_xyz (double *inr_outx,
+           double *ing_outy,
+           double *inb_outz)
+{
+  const double r = *inr_outx;
+  const double g = *ing_outy;
+  const double b = *inb_outz;
+
+  *inr_outx = Mrgb_to_xyz[0][0]*r + Mrgb_to_xyz[0][1]*g + Mrgb_to_xyz[0][2]*b;
+  *ing_outy = Mrgb_to_xyz[1][0]*r + Mrgb_to_xyz[1][1]*g + Mrgb_to_xyz[1][2]*b;
+  *inb_outz = Mrgb_to_xyz[2][0]*r + Mrgb_to_xyz[2][1]*g + Mrgb_to_xyz[2][2]*b;
+}
+
+
+static inline double
+ffunc(const double t)
+{
+  if (t > 0.008856F)
+    {
+      return (cbrt(t));
+    }
+  else
+    {
+      return (7.787F * t + 16.0F/116.0F);
+    }
+}
+
+
+static inline double
+ffunc_inv(const double t)
+{
+  if (t > 0.206893F)
+    {
+      return (t * t * t);
+    }
+  else
+    {
+      return ((t - 16.0F/116.0F) / 7.787F);
+    }
+}
+
+
+static void
+xyz_to_lab (double *inx,
+           double *iny,
+           double *inz)
+{
+  double L,a,b;
+  double ffuncY;
+  const double X = *inx;
+  const double Y = *iny;
+  const double Z = *inz;
+
+  if (Y > 0.0F)
+    {
+      if (Y > 0.008856F)
+       {
+         L = (116.0F * cbrt(Y)) - 16.0F;
+       }
+      else
+       {
+         L = (Y * 903.3F);
+       }
+
+#ifdef SANITY
+      if (L < 0.0F)
+       {
+         g_printerr (" <eek1>%f \007",(float)L);
+       }
+
+      if (L > 100.0F)
+       {
+         g_printerr (" <eek2>%f \007",(float)L);
+       }
+#endif
+    }
+  else
+    {
+      L = 0.0;
+    }
+
+  ffuncY = ffunc(Y);
+  a = 500.0F * (ffunc(X/xnn) - ffuncY);
+  b = 200.0F * (ffuncY - ffunc(Z/znn));
+
+  *inx = L;
+  *iny = a;
+  *inz = b;
+}
+
+
+static void
+lab_to_xyz (double *inl,
+           double *ina,
+           double *inb)
+{
+  double X,Y,Z;
+  double P;
+  const double L = *inl;
+  const double a = *ina;
+  const double b = *inb;
+
+  if (L > LRAMP)
+    {
+      P = Y = (L + 16.0F) / 116.0F;
+      Y = Y * Y * Y;
+    }
+  else
+    {
+      Y = L / 903.3F;
+      P = 7.787F * Y + 16.0F/116.0F;
+    }
+
+  X = (P + a / 500.0F);
+  X = xnn * ffunc_inv(X);
+  Z = (P - b / 200.0F);
+  Z = znn * ffunc_inv(Z);
+
+#ifdef SANITY
+  if (X<-0.00000F)
+    {
+      if (X<-0.0001F)
+       g_printerr ("{badX %f {%f,%f,%f}}",X,L,a,b);
+      X = 0.0F;
+    }
+  if (Y<-0.00000F)
+    {
+      if (Y<-0.0001F)
+       g_printerr ("{badY %f}",Y);
+      Y = 0.0F;
+    }
+  if (Z<-0.00000F)
+    {
+      if (Z<-0.1F)
+       g_printerr ("{badZ %f}",Z);
+      Z = 0.0F;
+    }
+#endif
+
+  *inl = X;
+  *ina = Y;
+  *inb = Z;
+}
+
+
+
+/* call this before using the CPercep function */
+void
+cpercep_init (void)
+{
+  static gboolean initialized = FALSE;
+
+  if (! initialized)
+    {
+      rgbxyzrgb_init();
+      initialized = TRUE;
+    }
+}
+
+void
+cpercep_rgb_to_space (double  inr,
+                      double  ing,
+                      double  inb,
+                     double *outr,
+                      double *outg,
+                      double *outb)
+{
+#ifdef APPROX
+#ifdef SANITY
+  /* ADM extra sanity */
+  if ((inr) > 255.0F ||
+      (ing) > 255.0F ||
+      (inb) > 255.0F ||
+      (inr) < -0.0F ||
+      (ing) < -0.0F ||
+      (inb) < -0.0F
+      )
+    abort();
+#endif /* SANITY */
+  inr = powtable[(int)inr];
+  ing = powtable[(int)ing];
+  inb = powtable[(int)inb];
+#else
+#ifdef SRGB
+  /* sRGB gamma curve */
+  if (inr <= (0.03928F * 255.0F))
+      inr = inr / (255.0F * 12.92F);
+  else
+      inr = pow( (inr + (0.055F * 255.0F)) / (1.055F * 255.0F), 2.4F);
+
+  if (ing <= (0.03928F * 255.0F))
+      ing = ing / (255.0F * 12.92F);
+  else
+      ing = pow( (ing + (0.055F * 255.0F)) / (1.055F * 255.0F), 2.4F);
+
+  if (inb <= (0.03928F * 255.0F))
+      inb = inb / (255.0F * 12.92F);
+  else
+      inb = pow( (inb + (0.055F * 255.0F)) / (1.055F * 255.0F), 2.4F);
+#else
+  /* pure gamma function */
+
+  /*  babl uses normalized RGB
+  inr = pow((inr)/255.0F, ASSUMED_GAMMA);
+  ing = pow((ing)/255.0F, ASSUMED_GAMMA);
+  inb = pow((inb)/255.0F, ASSUMED_GAMMA);
+  */
+#endif /* SRGB */
+#endif /* APPROX */
+
+#ifdef SANITY
+  /* ADM extra sanity */
+  if ((inr) > 1.0F ||
+      (ing) > 1.0F ||
+      (inb) > 1.0F ||
+      (inr) < 0.0F ||
+      (ing) < 0.0F ||
+      (inb) < 0.0F
+      )
+    {
+      g_printerr ("%%");
+      /* abort(); */
+    }
+#endif /* SANITY */
+
+  rgb_to_xyz(&inr, &ing, &inb);
+
+#ifdef SANITY
+  if (inr < 0.0F || ing < 0.0F || inb < 0.0F)
+    {
+      g_printerr (" [BAD2 XYZ: %f,%f,%f]\007 ",
+             inr,ing,inb);
+    }
+#endif /* SANITY */
+
+  xyz_to_lab(&inr, &ing, &inb);
+
+  *outr = inr;
+  *outg = ing;
+  *outb = inb;
+}
+
+
+void
+cpercep_space_to_rgb (double  inr,
+                      double  ing,
+                      double  inb,
+                     double *outr,
+                      double *outg,
+                      double *outb)
+{
+  lab_to_xyz(&inr, &ing, &inb);
+
+#ifdef SANITY
+  if (inr<-0.0F || ing<-0.0F || inb<-0.0F)
+    {
+      g_printerr (" [BAD1 XYZ: %f,%f,%f]\007 ",
+             inr,ing,inb);
+    }
+#endif
+
+  xyz_to_rgb(&inr, &ing, &inb);
+
+  /* yes, essential.  :( */
+  inr = CLAMP(inr,0.0F,1.0F);
+  ing = CLAMP(ing,0.0F,1.0F);
+  inb = CLAMP(inb,0.0F,1.0F);
+
+#ifdef SRGB
+  if (inr <= 0.0030402477F)
+    inr = inr * (12.92F * 255.0F);
+  else
+    inr = pow(inr, 1.0F/2.4F) * (1.055F * 255.0F) - (0.055F * 255.0F);
+
+  if (ing <= 0.0030402477F)
+    ing = ing * (12.92F * 255.0F);
+  else
+    ing = pow(ing, 1.0F/2.4F) * (1.055F * 255.0F) - (0.055F * 255.0F);
+
+  if (inb <= 0.0030402477F)
+    inb = inb * (12.92F * 255.0F);
+  else
+    inb = pow(inb, 1.0F/2.4F) * (1.055F * 255.0F) - (0.055F * 255.0F);
+#else
+  /* babl uses normalized RGB values
+  inr = 255.0F * pow(inr, REV_GAMMA);
+  ing = 255.0F * pow(ing, REV_GAMMA);
+  inb = 255.0F * pow(inb, REV_GAMMA);
+  */
+#endif
+
+  *outr = inr;
+  *outg = ing;
+  *outb = inb;
+}
+
+
+#if 0
+/* EXPERIMENTAL SECTION */
+
+const double
+xscaler(const double start, const double end,
+       const double me, const double him)
+{
+  return start + ((end-start) * him) / (me + him);
+}
+
+
+void
+mix_colours (const double L1, const double a1, const double b1,
+            const double L2, const double a2, const double b2,
+            double *rtnL, double *rtna, double *rtnb,
+            double mass1, double mass2)
+{
+  double w1, w2;
+
+#if 0
+  *rtnL = xscaler (L1, L2, mass1, mass2);
+  *rtna = xscaler (a1, a2, mass1, mass2);
+  *rtnb = xscaler (b1, b2, mass1, mass2);
+#else
+
+#if 1
+  w1 = mass1 * L1;
+  w2 = mass2 * L2;
+#else
+  w1 = mass1 * (L1*L1*L1);
+  w2 = mass2 * (L2*L2*L2);
+#endif
+
+  *rtnL = xscaler (L1, L2, mass1, mass2);
+
+  if (w1 <= 0.0 &&
+      w2 <= 0.0)
+    {
+      *rtna =
+       *rtnb = 0.0;
+#ifdef SANITY
+      /* g_printerr ("\007OUCH. "); */
+#endif
+    }
+  else
+    {
+      *rtna = xscaler(a1, a2, w1, w2);
+      *rtnb = xscaler(b1, b2, w1, w2);
+    }
+#endif
+}
+#endif /* EXPERIMENTAL SECTION */
diff --git a/babl/base/cpercep.h b/babl/base/cpercep.h
new file mode 100644 (file)
index 0000000..82bc5c0
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+Copyright (C) 1997-2002 Adam D. Moss (the "Author").  All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Author of the
+Software shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization
+from the Author.
+*/
+
+/*
+  cpercep.c: The CPercep Functions v0.9: 2002-02-10
+  Adam D. Moss: adam@gimp.org <http://www.foxbox.org/adam/code/cpercep/>
+
+  TODO: document functions, rename erroneously-named arguments
+*/
+
+#ifndef __CPERCEP_H__
+#define __CPERCEP_H__
+
+
+void  cpercep_init         (void);
+
+void  cpercep_rgb_to_space (double  inr,
+                            double  ing,
+                            double  inb,
+                            double *outr,
+                            double *outg,
+                            double *outb);
+
+void  cpercep_space_to_rgb (double  inr,
+                            double  ing,
+                            double  inb,
+                            double *outr,
+                            double *outg,
+                            double *outb);
+
+
+#if 0
+/* This is in the header so that it can potentially be inlined. */
+static const double
+cpercep_distance_space (const double L1, const double a1, const double b1,
+                       const double L2, const double a2, const double b2)
+{
+  const double Ld = L1 - L2;
+  const double ad = a1 - a2;
+  const double bd = b1 - b2;
+
+  return (Ld*Ld + ad*ad + bd*bd);
+}
+#endif
+
+
+#endif /* __CPERCEP_H__ */
index d74f3fcfdfffc3e97b73a9e2623ca29f3b8c85b3..4c10122c3a49474ee7df1afd252d7c395fc5cf68 100644 (file)
@@ -95,6 +95,110 @@ models (void)
   );
 }
 
+static void
+rgb_to_cmyk (int    src_bands,
+             void **src,
+             int   *src_pitch,
+             int    dst_bands,
+             void **dst,
+             int   *dst_pitch,
+             int    n)
+{
+  BABL_PLANAR_SANITY
+
+  while (n--)
+    {
+      double red   = *(double*)src[0];
+      double green = *(double*)src[1];
+      double blue  = *(double*)src[2];
+
+      double cyan, magenta, yellow, key;
+
+      double pullout = 1.0;
+
+      cyan    = 1.0 - red;
+      magenta = 1.0 - green;
+      yellow  = 1.0 - blue;
+
+      key = 1.0;
+      if (cyan    < key) key = cyan;
+      if (magenta < key) key = magenta;
+      if (yellow  < key) key = yellow;
+
+      key *= pullout;
+
+      if (key < 1.0)
+        {
+          cyan    = (cyan - key)    / (1.0 -key);
+          magenta = (magenta - key) / (1.0 -key);
+          yellow  = (yellow - key)  / (1.0 -key);
+        }
+      else
+        {
+          cyan    = 0.0;
+          magenta = 0.0;
+          yellow  = 0.0;
+        }
+
+      *(double*)dst[0] = cyan;
+      *(double*)dst[1] = magenta;
+      *(double*)dst[2] = yellow;
+      *(double*)dst[3] = key;
+
+      if (dst_bands > 4)               /* alpha passthorugh */
+        *(double*)dst[4] = (src_bands>3)?*(double*)src[3]:1.0;
+
+      BABL_PLANAR_STEP
+    }
+}
+
+static void
+cmyk_to_rgb (int    src_bands,
+             void **src,
+             int   *src_pitch,
+             int    dst_bands,
+             void **dst,
+             int   *dst_pitch,
+             int    n)
+{
+  BABL_PLANAR_SANITY
+
+  while (n--)
+    {
+      double cyan    = *(double*)src[0];
+      double yellow  = *(double*)src[1];
+      double magenta = *(double*)src[2];
+      double key     = *(double*)src[3];
+
+      double red, green, blue;
+
+      if (key < 1.0)
+        {
+          cyan    = cyan    * (1.0 - key) + key;
+          magenta = magenta * (1.0 - key) + key;
+          yellow  = yellow  * (1.0 - key) + key;
+        }
+      else
+        {
+          cyan = magenta = yellow = 1.0;
+        }
+
+      red   = 1.0 - cyan;
+      green = 1.0 - magenta;
+      blue  = 1.0 - yellow;
+
+      *(double*)dst[0] = red;
+      *(double*)dst[1] = green;
+      *(double*)dst[2] = blue;
+
+      if (dst_bands > 3)               /* alpha passthorugh */
+        *(double*)dst[3] = (src_bands>4)?*(double*)src[4]:1.0;
+
+      BABL_PLANAR_STEP
+    }
+}
+
+#if 0
 static void
 rgb_to_and_from_cmy (int    src_bands,
                      void **src,
@@ -124,6 +228,7 @@ rgb_to_and_from_cmy (int    src_bands,
       BABL_PLANAR_STEP
     }
 }
+#endif
 
 static void
 conversions (void)
@@ -131,17 +236,17 @@ conversions (void)
   babl_conversion_new (
     "babl-base: rgba to cmy",
     "source",      babl_model_id (BABL_RGBA),
-    "destination", babl_model_id (BABL_CMY),
-    "planar",      rgb_to_and_from_cmy,
+    "destination", babl_model_id (BABL_CMYK),
+    "planar",      rgb_to_cmyk,
     NULL
   );
 
 
   babl_conversion_new (
     "babl-base: cmy to rgba",
-    "source",      babl_model_id (BABL_CMY),
+    "source",      babl_model_id (BABL_CMYK),
     "destination", babl_model_id (BABL_RGBA),
-    "planar",      rgb_to_and_from_cmy,
+    "planar",      cmyk_to_rgb,
     NULL
   );
 }
@@ -149,27 +254,5 @@ conversions (void)
 static void
 pixel_formats (void)
 {
-  babl_pixel_format_new (
-    "cmyk-float",
-    "id",             BABL_CMYK_FLOAT,
-    babl_model_id     (BABL_CMYK),
-    babl_type_id      (BABL_FLOAT),
-    babl_component_id (BABL_CYAN), 
-    babl_component_id (BABL_MAGENTA), 
-    babl_component_id (BABL_YELLOW),
-    babl_component_id (BABL_KEY),
-    NULL);
-
-  babl_pixel_format_new (
-    "cmyka-float",
-    "id",             BABL_CMYKA_FLOAT,
-    babl_model_id     (BABL_CMYKA),
-    babl_type_id      (BABL_FLOAT),
-    babl_component_id (BABL_CYAN), 
-    babl_component_id (BABL_MAGENTA), 
-    babl_component_id (BABL_YELLOW),
-    babl_component_id (BABL_KEY),
-    babl_component_id (BABL_ALPHA),
-    NULL);
 }
 
diff --git a/babl/base/model-lab.c b/babl/base/model-lab.c
new file mode 100644 (file)
index 0000000..f08cf9d
--- /dev/null
@@ -0,0 +1,197 @@
+/* babl - dynamically extendable universal pixel conversion library.
+ * Copyright (C) 2005, Øyvind Kolås.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include "babl.h"
+#include "util.h"
+#include "cpercep.h"
+
+static void components    (void);
+static void models        (void);
+static void conversions   (void);
+static void pixel_formats (void);
+
+void
+babl_base_model_lab (void)
+{
+  cpercep_init  ();
+  components    ();
+  models        ();
+  conversions   ();
+  pixel_formats ();
+}
+
+static void
+components (void)
+{
+  babl_component_new (
+   "L",
+   "id",    BABL_LAB_L,
+   "luma",
+   NULL);
+
+  babl_component_new (
+   "a",
+   "id",    BABL_LAB_A,
+   "chroma",
+   NULL);
+
+  babl_component_new (
+   "b",
+   "id",    BABL_LAB_B,
+   "chroma",
+   NULL);
+}
+
+static void
+models (void)
+{
+  babl_model_new (
+    "CIE Lab",
+    "id", BABL_LAB,
+    babl_component_id (BABL_LAB_L),
+    babl_component_id (BABL_LAB_A),
+    babl_component_id (BABL_LAB_B),
+    NULL);
+
+  babl_model_new (
+    "CIE Lab alpha",
+    "id", BABL_LAB_ALPHA,
+    babl_component_id (BABL_LAB_L),
+    babl_component_id (BABL_LAB_A),
+    babl_component_id (BABL_LAB_B),
+    babl_component_id (BABL_ALPHA),
+    NULL);
+}
+
+static void
+rgb_to_lab (int    src_bands,
+            void **src,
+            int   *src_pitch,
+            int    dst_bands,
+            void **dst,
+            int   *dst_pitch,
+            int    n)
+{
+  BABL_PLANAR_SANITY
+
+  while (n--)
+    {
+      double red   = *(double*)src[0];
+      double green = *(double*)src[1];
+      double blue  = *(double*)src[2];
+
+      double L, a, b;
+
+      cpercep_rgb_to_space (red, green, blue, &L, &a, &b);
+
+      *(double*)dst[0] = L;
+      *(double*)dst[1] = a;
+      *(double*)dst[2] = b;
+
+      if (dst_bands > 3)               /* alpha passthorugh */
+        *(double*)dst[3] = (src_bands>3)?*(double*)src[3]:1.0;
+
+      BABL_PLANAR_STEP
+    }
+}
+
+static void
+lab_to_rgb (int    src_bands,
+            void **src,
+            int   *src_pitch,
+            int    dst_bands,
+            void **dst,
+            int   *dst_pitch,
+            int    n)
+{
+  BABL_PLANAR_SANITY
+
+  while (n--)
+    {
+      double L = *(double*)src[0];
+      double a = *(double*)src[1];
+      double b = *(double*)src[2];
+
+      double red, green, blue;
+
+      cpercep_space_to_rgb (L, a, b, &red, &green, &blue);
+
+      *(double*)dst[0] = red;
+      *(double*)dst[1] = green;
+      *(double*)dst[2] = blue;
+
+      if (dst_bands > 3)               /* alpha passthorugh */
+        *(double*)dst[3] = (src_bands>3)?*(double*)src[3]:1.0;
+
+      BABL_PLANAR_STEP
+    }
+}
+
+static void
+conversions (void)
+{
+  babl_conversion_new (
+    "babl-base: rgba to lab",
+    "source",      babl_model_id (BABL_RGBA),
+    "destination", babl_model_id (BABL_LAB),
+    "planar",      rgb_to_lab,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: lab to rgba",
+    "source",      babl_model_id (BABL_LAB),
+    "destination", babl_model_id (BABL_RGBA),
+    "planar",      lab_to_rgb,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: rgb to lab",
+    "source",      babl_model_id (BABL_RGB),
+    "destination", babl_model_id (BABL_LAB),
+    "planar",      rgb_to_lab,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: lab to rgb",
+    "source",      babl_model_id (BABL_LAB),
+    "destination", babl_model_id (BABL_RGB),
+    "planar",      lab_to_rgb,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: rgba to laba",
+    "source",      babl_model_id (BABL_RGBA),
+    "destination", babl_model_id (BABL_LAB_ALPHA),
+    "planar",      rgb_to_lab,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: laba to rgba",
+    "source",      babl_model_id (BABL_LAB_ALPHA),
+    "destination", babl_model_id (BABL_RGBA),
+    "planar",      lab_to_rgb,
+    NULL
+  );
+}
+
+static void
+pixel_formats (void)
+{
+}
index c1435586805be15d8c73bc9f953c0091e0ee07bd..8d5f28daf49a98410622569952e97ed0f437c4c9 100644 (file)
@@ -420,7 +420,7 @@ pixel_formats (void)
     "rgba-double",
     "id",              BABL_RGBA_DOUBLE,
     babl_model_id     (BABL_RGBA),
-    babl_type_id      (BABL_FLOAT),
+    babl_type_id      (BABL_DOUBLE),
     babl_component_id (BABL_RED), 
     babl_component_id (BABL_GREEN), 
     babl_component_id (BABL_BLUE),
index 093247f8d58458b9cba1dd1608423237eb6f92cf..feb0d58f46ef019b37524941d51e1d7d0209bb53 100644 (file)
@@ -41,14 +41,14 @@ babl_base_model_ycbcr (void)
 static void
 components (void)
 {
-    babl_component_new (
-   "Cb",
+  babl_component_new (
+   "cb",
    "id",    BABL_CB,
    "chroma",
    NULL);
 
   babl_component_new (
-   "Cr",
+   "cr",
    "id",    BABL_CR,
    "chroma",
    NULL);
@@ -57,11 +57,144 @@ components (void)
 static void
 models (void)
 {
+  babl_model_new (
+    "ycbcr",
+    "id", BABL_YCBCR,
+    babl_component_id (BABL_LUMINANCE),
+    babl_component_id (BABL_CB),
+    babl_component_id (BABL_CR),
+    NULL);
+
+  babl_model_new (
+    "ycbcra",
+    "id", BABL_YCBCRA,
+    babl_component_id (BABL_LUMINANCE),
+    babl_component_id (BABL_CB),
+    babl_component_id (BABL_CR),
+    babl_component_id (BABL_ALPHA),
+    NULL);
+}
+
+static void
+rgb_to_ycbcr (int    src_bands,
+              void **src,
+              int   *src_pitch,
+              int    dst_bands,
+              void **dst,
+              int   *dst_pitch,
+              int    n)
+{
+  BABL_PLANAR_SANITY
+
+  while (n--)
+    {
+      double red   = *(double*)src[0];
+      double green = *(double*)src[1];
+      double blue  = *(double*)src[2];
+
+      double luminance, cb, cr;
+
+      luminance =   0.299   * red   +
+                    0.587   * green +
+                    0.114   * blue;
+      cb        = (-0.1687) * red   
+                   -0.3313  * green +
+                    0.5     * blue;
+      cr        =   0.5     * red   
+                   -0.4187  * green +
+                   -0.0813  * blue;
+
+      *(double*)dst[0] = luminance;
+      *(double*)dst[1] = cb;
+      *(double*)dst[2] = cr;
+
+      if (dst_bands > 3)               /* alpha passthorugh */
+        *(double*)dst[3] = (src_bands>3)?*(double*)src[3]:1.0;
+
+      BABL_PLANAR_STEP
+    }
+}
+
+static void
+ycbcr_to_rgb (int    src_bands,
+              void **src,
+              int   *src_pitch,
+              int    dst_bands,
+              void **dst,
+              int   *dst_pitch,
+              int    n)
+{
+  BABL_PLANAR_SANITY
+
+  while (n--)
+    {
+      double luminance = *(double*)src[0];
+      double cb        = *(double*)src[1];
+      double cr        = *(double*)src[2];
+
+      double red, green, blue;
+
+      red   = luminance + 1.40200 * cr;
+      green = luminance - 0.34414 * cb - 0.71414 *cr;
+      blue  = luminance + 1.77200 * cb;
+
+
+      *(double*)dst[0] = red;
+      *(double*)dst[1] = green;
+      *(double*)dst[2] = blue;
+
+      if (dst_bands > 3)               /* alpha passthorugh */
+        *(double*)dst[3] = (src_bands>3)?*(double*)src[3]:1.0;
+
+      BABL_PLANAR_STEP
+    }
 }
 
 static void
 conversions (void)
 {
+  babl_conversion_new (
+    "babl-base: rgba to ycbcr",
+    "source",      babl_model_id (BABL_RGBA),
+    "destination", babl_model_id (BABL_YCBCR),
+    "planar",      rgb_to_ycbcr,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: ycbcr to rgba",
+    "source",      babl_model_id (BABL_YCBCR),
+    "destination", babl_model_id (BABL_RGBA),
+    "planar",      ycbcr_to_rgb,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: rgb to ycbcr",
+    "source",      babl_model_id (BABL_RGB),
+    "destination", babl_model_id (BABL_YCBCR),
+    "planar",      rgb_to_ycbcr,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: ycbcr to rgb",
+    "source",      babl_model_id (BABL_YCBCR),
+    "destination", babl_model_id (BABL_RGB),
+    "planar",      ycbcr_to_rgb,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: rgba to ycbcra",
+    "source",      babl_model_id (BABL_RGBA),
+    "destination", babl_model_id (BABL_YCBCRA),
+    "planar",      rgb_to_ycbcr,
+    NULL
+  );
+  babl_conversion_new (
+    "babl-base: ycbcra to rgba",
+    "source",      babl_model_id (BABL_YCBCRA),
+    "destination", babl_model_id (BABL_RGBA),
+    "planar",      ycbcr_to_rgb,
+    NULL
+  );
 }
 
 static void
index 2b602b01820e64294721f49bbaa1a95f84e017d9..f1850f020e9435dc29beed08e6f97e5244414bb9 100644 (file)
@@ -51,7 +51,7 @@ convert_u16_double (void *src,
 {
   while (n--)
     {
-      (*(double *) dst) = (*(unsigned short *) src*65535.0);
+      (*(double *) dst) = (*(unsigned short *) src / 65535.0);
       dst += 8;
       src += 2;
     }
index 081b449167b6bd4f2862138fb0279933fdc84d4a..6774d7d409ccc7a79fd9736f00fdb9b3a3eae535 100644 (file)
@@ -1,11 +1,17 @@
 TESTS =                                \
        float_to_u8             \
        grayscale_to_rgb        \
-       u8_to_float
-
-float_to_u8_SOURCES      =  float_to_u8.c
-u8_to_float_SOURCES      =  u8_to_float.c
-grayscale_to_rgb_SOURCES =  grayscale_to_rgb.c
+       u8_to_float             \
+       rgb_to_lab_to_rgb       \
+       rgb_to_ycbcr            \
+       rgb_to_ycbcr_to_rgb
+
+float_to_u8_SOURCES         = float_to_u8.c
+u8_to_float_SOURCES         = u8_to_float.c
+grayscale_to_rgb_SOURCES    = grayscale_to_rgb.c
+rgb_to_lab_to_rgb_SOURCES   = rgb_to_lab_to_rgb.c
+rgb_to_ycbcr_SOURCES        = rgb_to_ycbcr.c
+rgb_to_ycbcr_to_rgb_SOURCES = rgb_to_ycbcr_to_rgb.c
 
 
 AM_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/babl
diff --git a/tests/rgb_to_lab_to_rgb.c b/tests/rgb_to_lab_to_rgb.c
new file mode 100644 (file)
index 0000000..063a9fe
--- /dev/null
@@ -0,0 +1,99 @@
+/* babl - dynamically extendable universal pixel conversion library.
+ * Copyright (C) 2005, Øyvind Kolås.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <math.h>
+#include "babl.h"
+#include "babl-internal.h"
+
+#define PIXELS 6
+#define TOLERANCE 0.001 
+
+float source_buf [PIXELS*3]=
+  {0.0, 0.0, 0.0,
+   0.5, 0.5, 0.5,
+   1.0, 1.0, 1.0,
+   1.0, 0.0, 0.0,
+   0.0, 1.0, 0.0,
+   0.0, 0.0, 1.0};
+
+float temp_buf        [PIXELS*3];
+float destination_buf [PIXELS*3];
+
+int
+test (void)
+{
+  BablFish *fish;
+  int       i;
+  int      OK=1;
+
+  
+  fish = babl_fish (
+    (Babl*) babl_pixel_format_new (
+      "foo",
+      babl_model ("rgb"),
+      babl_type ("float"),
+      babl_component ("red"),
+      babl_component ("green"),
+      babl_component ("blue"),
+      NULL
+    ),
+    (Babl*) babl_pixel_format_new (
+      "bar",
+      babl_model ("CIE Lab"),
+      babl_type ("float"), 
+      babl_component ("L"),
+      babl_component ("a"),
+      babl_component ("b"),
+      NULL
+    )
+  );
+
+  babl_fish_process (fish, source_buf, temp_buf, PIXELS);
+
+  fish = babl_fish ((Babl*) babl_pixel_format ("bar"),
+                    (Babl*) babl_pixel_format ("foo"));
+  
+  babl_fish_process (fish, temp_buf, destination_buf, PIXELS);
+
+  for (i=0; i<PIXELS * 3; i++)
+    {
+      if (fabs(destination_buf[i] - source_buf[i]) > TOLERANCE)
+        {
+          babl_log ("%2i (%2i%%3=%i, %2i/3=%i) is %f should be %f",
+                      i, i,i%3,    i,i/3,  destination_buf[i], source_buf[i]);
+          OK=0;
+        }
+    }
+
+  if (!OK)
+    return -1;
+  return 0;
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  babl_init ();
+  if (test())
+    return -1;
+  babl_destroy ();
+  return 0;
+}
diff --git a/tests/rgb_to_ycbcr.c b/tests/rgb_to_ycbcr.c
new file mode 100644 (file)
index 0000000..4cbef79
--- /dev/null
@@ -0,0 +1,101 @@
+/* babl - dynamically extendable universal pixel conversion library.
+ * Copyright (C) 2005, Øyvind Kolås.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <math.h>
+#include "babl.h"
+#include "babl-internal.h"
+
+#define PIXELS 6
+#define TOLERANCE 0.000001
+
+float source_buf [PIXELS*3]=
+  {0.0, 0.0, 0.0,
+   0.5, 0.5, 0.5,
+   1.0, 1.0, 1.0,
+   1.0, 0.0, 0.0,
+   0.0, 1.0, 0.0,
+   0.0, 0.0, 1.0};
+
+float reference_buf [PIXELS*3]=
+  {0.0,   0.0,    0.0,
+   0.5,   0.0,    0.0,
+   1.0,   0.0,    0.0,
+   0.299,-0.1687, 0.5,
+   0.587,-0.3313,-0.4187,
+   0.114, 0.5,   -0.081300};
+
+
+float destination_buf [PIXELS*3];
+
+int
+test (void)
+{
+  BablFish *fish;
+  int       i;
+  int      OK=1;
+
+  
+  fish = babl_fish (
+    (Babl*) babl_pixel_format_new (
+      "foo",
+      babl_model ("rgb"),
+      babl_type ("float"),
+      babl_component ("red"),
+      babl_component ("green"),
+      babl_component ("blue"),
+      NULL
+    ),
+    (Babl*) babl_pixel_format_new (
+      "bar",
+      babl_model ("ycbcr"),
+      babl_type ("float"),
+      babl_component ("luminance"),
+      babl_component ("cb"),
+      babl_component ("cr"),
+      NULL
+    )
+  );
+
+  babl_fish_process (fish, source_buf, destination_buf, PIXELS);
+  
+  for (i=0; i<PIXELS * 3; i++)
+    {
+      if (fabs(destination_buf[i] - reference_buf[i]) > TOLERANCE)
+        {
+          babl_log ("%2i (%2i%%3=%i, %2i/3=%i) is %f should be %f",
+                      i, i,i%3,    i,i/3,  destination_buf[i], reference_buf[i]);
+          OK=0;
+        }
+    }
+  if (!OK)
+    return -1;
+  return 0;
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  babl_init ();
+  if (test())
+    return -1;
+  babl_destroy ();
+  return 0;
+}
diff --git a/tests/rgb_to_ycbcr_to_rgb.c b/tests/rgb_to_ycbcr_to_rgb.c
new file mode 100644 (file)
index 0000000..d78ecb5
--- /dev/null
@@ -0,0 +1,98 @@
+/* babl - dynamically extendable universal pixel conversion library.
+ * Copyright (C) 2005, Øyvind Kolås.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <math.h>
+#include "babl.h"
+#include "babl-internal.h"
+
+#define PIXELS 6
+#define TOLERANCE 0.001 
+
+float source_buf [PIXELS*3]=
+  {0.0, 0.0, 0.0,
+   0.5, 0.5, 0.5,
+   1.0, 1.0, 1.0,
+   1.0, 0.0, 0.0,
+   0.0, 1.0, 0.0,
+   0.0, 0.0, 1.0};
+
+float temp_buf        [PIXELS*3];
+float destination_buf [PIXELS*3];
+
+int
+test (void)
+{
+  BablFish *fish;
+  int       i;
+  int      OK=1;
+
+  
+  fish = babl_fish (
+    (Babl*) babl_pixel_format_new (
+      "foo",
+      babl_model ("rgb"),
+      babl_type ("float"),
+      babl_component ("red"),
+      babl_component ("green"),
+      babl_component ("blue"),
+      NULL
+    ),
+    (Babl*) babl_pixel_format_new (
+      "bar",
+      babl_model ("ycbcr"),
+      babl_type ("float"), 
+      babl_component ("luminance"),
+      babl_component ("cb"),
+      babl_component ("cr"),
+      NULL
+    )
+  );
+
+  babl_fish_process (fish, source_buf, temp_buf, PIXELS);
+
+  fish = babl_fish ((Babl*) babl_pixel_format ("bar"),
+                    (Babl*) babl_pixel_format ("foo"));
+  
+  babl_fish_process (fish, temp_buf, destination_buf, PIXELS);
+
+  for (i=0; i<PIXELS * 3; i++)
+    {
+      if (fabs(destination_buf[i] - source_buf[i]) > TOLERANCE)
+        {
+          babl_log ("%2i (%2i%%3=%i, %2i/3=%i) is %f should be %f",
+                      i, i,i%3,    i,i/3,  destination_buf[i], source_buf[i]);
+          OK=0;
+        }
+    }
+  if (!OK)
+    return -1;
+  return 0;
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  babl_init ();
+  if (test())
+    return -1;
+  babl_destroy ();
+  return 0;
+}